package org.budo.maven.plugin.assembly.mojo;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.maven.model.Profile;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.project.MavenProject;
import org.budo.support.lang.util.FileUtil;
import org.budo.support.lang.util.IoUtil;
import org.budo.support.lang.util.PropertiesUtil;
import org.budo.support.lang.util.StringUtil;
import org.budo.time.Time;

/**
 * @author limingwei
 */
public abstract class AbstractBudoMavenAssemblyMojo extends AbstractMojo {
    private static final String JAVA_OPTS = "-Xrs -server -Xmx512m -Xms64m -XX:MaxPermSize=256m -XX:PermSize=16m -Xss256k -XX:SurvivorRatio=8 -XX:LargePageSizeInBytes=128m";

    protected static final String README_TEMPLATE = "README\r\n\r\n" //
            + "bin: the script to start & stop\r\n\r\n" //
            + "conf: configuration files\r\n\r\n" //
            + "lib: the jars\r\n\r\n" //
            + "webapp: html & web.xml\r\n\r\n";

    public abstract MavenProject getProject();

    /**
     * 替换脚本模板中的变量
     */
    protected String mergeTemplate(String scriptTemplate) {
        Properties properties = this.defaultProperties();

        Properties automanProperties = this.automanProperties();
        for (Entry<Object, Object> entry : automanProperties.entrySet()) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();

            String k = key.replace("automan.", "");
            properties.put(k, value); // 去前缀，合并
        }

        // 替换变量
        for (Entry<Object, Object> entry : properties.entrySet()) {
            String key = (String) entry.getKey();
            String value = (String) entry.getValue();

            scriptTemplate = scriptTemplate.replace("#{" + key + "}", value);
        }

        return scriptTemplate;
    }

    /**
     * 项目自定义配置，从 automan.conf 中读取
     */
    private Properties automanProperties() {
        String automanPath = FileUtil.getCanonicalPath(this.getProject().getBasedir()) //
                + File.separator + "src" + File.separator + "main" + File.separator + "resources" //
                + File.separator + "automan.conf";

        File automanFile = new File(automanPath);
        if (automanFile.exists() && automanFile.isFile() && automanFile.canRead()) {
            FileInputStream fileInputStream = IoUtil.fileInputStream(automanPath);
            return PropertiesUtil.load(fileInputStream);
        }

        return new Properties();
    }

    /**
     * 默认值
     */
    private Properties defaultProperties() {
        Properties properties = new Properties();
        properties.setProperty("AUTOMAN_JAVA_OPTS", JAVA_OPTS);
        properties.setProperty("AUTOMAN_HOST", "127.0.0.1");
        properties.setProperty("AUTOMAN_PORT", "-1");
        properties.setProperty("AUTOMAN_APPLICATION_NAME", this.getProject().getArtifactId());
        properties.setProperty("AUTOMAN_PROTOCOL", "not-dubbo");
        properties.setProperty("AUTOMAN_LAUNCHER", "budo.Launcher");

        String now = Time.now().toString("yyyy-MM-dd HH:mm:ss.SSS");
        properties.setProperty("BuildAt", now);
        return properties;
    }

    protected boolean matchAnyExcludes(File file, Resource resource) {
        for (String exclude : resource.getExcludes()) {
            String filePath = FileUtil.getCanonicalPath(file);
            String resourceDir = resource.getDirectory();
            if (this.match(filePath, resourceDir, exclude)) {
                return true; // 匹配到排除目录
            }
        }

        return false;
    }

    // 如果 match 就不复制
    private boolean match(String filePath, String resourceDir, String exclude) {
        if (exclude.startsWith("*.")) {
            String substring = exclude.substring(1);
            if (filePath.endsWith(substring)) {
                return true;
            }
        }

        if (exclude.startsWith("**/*.")) {
            String substring = exclude.substring(4);
            if (filePath.endsWith(substring)) {
                return true;
            }
        }

        if (exclude.endsWith("/*")) {
            String substring = exclude.substring(0, exclude.length() - 2);
            String prefix = resourceDir + "/" + substring;
            if (filePath.startsWith(prefix)) {
                return true;
            }
        }

        // this.getLog().warn("#126 exclude=" + exclude);
        return false;
    }

    protected String findInResource(String path) throws IOException {
        List<Resource> resources = this.getProject().getResources();

        for (Resource resource : resources) {
            String srcPath = resource.getDirectory() //
                    + File.separator + path;

            File file = new File(srcPath);
            if (file.exists()) {
                this.getLog().debug("#301 this will skip, srcPath=" + srcPath);
                return FileUtil.getCanonicalPath(file);
            }
        }

        this.getLog().debug("#306 this will be in the jar, path=" + path);
        return null;
    }

    protected void copyDirAndFiltering(File from, File to, FileFilter filter, String filtering, Properties activeProfileProperties) {
        if (!from.isDirectory()) {
            throw new RuntimeException("#15 from is not dir, from=" + from);
        }

        if (!to.exists()) {
            to.mkdirs();
        }

        File[] files = from.listFiles();

        for (File file : files) {
            if (null != filter && !filter.accept(file)) {
                continue;
            }

            if (file.isDirectory()) {
                this.copyDirAndFiltering(new File(from, file.getName()), new File(to, file.getName()), filter, filtering, activeProfileProperties);
                continue;
            }

            if (file.isFile()) {
                this.copyFileAndFiltering(new File(from, file.getName()), new File(to, file.getName()), filtering, activeProfileProperties);
                continue;
            }

            throw new RuntimeException("#79 file is not file or directory, file=" + file);
        }
    }

    private void copyFileAndFiltering(File from, File to, String filtering, Properties activeProfileProperties) {
        if (!to.exists()) {
            File parentFile = to.getParentFile();
            parentFile.mkdirs();
        }

        if ("true".equalsIgnoreCase(filtering)) {
            this.filterFile(from, to, activeProfileProperties);
        } else {
            IoUtil.write(from, to);
        }
    }

    protected void filterFile(File from, File to, Properties activeProfileProperties) {
        this.getLog().debug("#188 filterFile, from=" + from + ", to=" + to + ", properties=" + activeProfileProperties);

        // 被替换处理的行数
        int filteredLineCount = 0;

        List<String> lines = IoUtil.bufferedReadLineToStringList(IoUtil.fileInputStream(from));
        List<String> out = new ArrayList<String>();
        for (String line : lines) {
            String lineNew = null;
            int a = line.indexOf("${");
            if (a > 0) {
                int b = line.indexOf('}', a);
                if (b > 0) {
                    String left = line.substring(0, a);
                    String right = line.substring(b + 1);

                    String key = line.substring(a + 2, b);
                    String value = activeProfileProperties.getProperty(key);

                    if (null != value) {
                        lineNew = left + value + right;

                        this.getLog().info("#203 key=" + key + ", value=" + value + ", line=" + line + ", lineNew=" + lineNew);

                        filteredLineCount++;
                    }
                }
            }

            out.add(null == lineNew ? line : lineNew);
        }

        if (filteredLineCount > 0) {
            this.getLog().info("#217 from=" + from + ", to=" + FileUtil.getCanonicalPath(to) + ", properties=" + activeProfileProperties);
        }

        FileUtil.deleteIfExists(to);
        FileUtil.createNewFile(to);

        IoUtil.write(StringUtil.join(out, "\n").getBytes(), to);
    }

    protected Properties getActiveProfileProperties() {
        Properties properties = new Properties();

        List<Profile> activeProfiles = this.getProject().getActiveProfiles();
        if (null == activeProfiles || activeProfiles.isEmpty()) {
            return properties;
        }

        for (Profile profile : activeProfiles) {
            if (null == profile) {
                continue;
            }

            Properties prop = profile.getProperties();
            if (null == prop) {
                continue;
            }

            properties.putAll(prop);
        }

        return properties;
    }
}